﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Drawing2D;

namespace UMLDesigner
{
    class RelationView
    {
        protected static Pen myPen = new Pen(Color.Gray, 2);
        private static StringFormat alignRightFormat = new StringFormat();
        private static StringFormat alignCenterFormat = new StringFormat();
        
        protected ClassView iniClass;
        protected ClassView endClass;

        protected int endSymbolSize = 10;
        protected Point[] finalPolygon;

        public RelationView()
        {
            alignRightFormat.Alignment = StringAlignment.Far;
            alignCenterFormat.Alignment = StringAlignment.Center;
        }

        public void Paint(Graphics g)
        {
            Point iniP = iniClass.GetCenter(),
                  endP = endClass.GetCenter();

            int hDistance = Math.Abs(iniP.X - endP.X),
                hDirection = iniP.X > endP.X ? 1 : -1,  // 1: right, -1: left
                vDistance = Math.Abs(iniP.Y - endP.Y),
                vDirection = iniP.Y > endP.Y ? 1 : -1;  // 1: down, -1: up

            Point endCoord = endP, lineEndCoord, iniCardinalityCoord, endCardinalityCoord;
            float rotation = 0F;
            if (vDistance > hDistance)
            {
                //Paints the end of the relation at the top or at the bottom
                int dy = vDirection * endClass.GetHeight() / 2;
                lineEndCoord = new Point(endP.X, endP.Y + dy + vDirection * endSymbolSize);
                
                //Apply the calculated offset
                endCoord.Offset(0, dy);
                rotation = vDirection == 1 ? 0F : 180F;

                //Cardinality               
                iniCardinalityCoord = new Point(iniP.X, iniP.Y - vDirection * (20 + iniClass.GetHeight() / 2));
                endCardinalityCoord = new Point(endP.X, endP.Y + vDirection * (20 + endClass.GetHeight() / 2));
            }
            else
            {
                //Paints the end of the relation to the left or to the right
                int dx = hDirection * endClass.GetWidth() / 2;
                lineEndCoord = new Point(endP.X + dx + hDirection * endSymbolSize, endP.Y);

                //Apply the calculated offset
                endCoord.Offset(dx, 0);
                rotation = hDirection == 1 ? 270F : 90F;

                //Cardinality               
                iniCardinalityCoord = new Point(iniP.X - hDirection * (20 + iniClass.GetWidth() / 2), iniP.Y);
                endCardinalityCoord = new Point(endP.X + hDirection * (20 + endClass.GetWidth() / 2), endP.Y);
            }

            PaintLine(g, iniP, lineEndCoord);
            PaintEndSymbol(g, endCoord, rotation);
            PaintName(g, iniCardinalityCoord, endCardinalityCoord);
            PaintCardinalitiesAndAttributes(g, iniCardinalityCoord, endCardinalityCoord, rotation);
        }

        private void PaintCardinalitiesAndAttributes(Graphics g, Point iniP, Point endP, float rotation) {
            //Initial Cardinality and attribute name
            PaintCardinality(g, iniP, "0..*", rotation);
            PaintAttributeName(g, iniP, "iniAttrName", rotation);

            //Final Cardinality and attribute name
            PaintCardinality(g, endP, "1..*", 360-rotation);
            PaintAttributeName(g, endP, "endAttrName", 360 - rotation);
        }

        private void PaintAttributeName(Graphics g, Point p, string name)
        {
            g.DrawString(name, SystemFonts.DefaultFont, Brushes.Black, p);
        }

        private void PaintAttributeName(Graphics g, Point p, string card, float rotation)
        {
            int dy = 0, dx = 0;
            StringFormat format = StringFormat.GenericDefault;
            switch ((int)rotation)
            {
                case 0:
                case 360: format = alignRightFormat; dx = -5; dy = -8; break;
                case 90:  break;
                case 180: dx = 5; dy = -8; break;
                case 270: format = alignRightFormat; break;
            }

            Point pMod = new Point(p.X + dx, p.Y + dy);
            g.DrawString(card, SystemFonts.DefaultFont, Brushes.Black, pMod, format);
        }

        private void PaintCardinality(Graphics g, Point p, string card, float rotation) 
        {
            int dy = 0, dx = 0;
            StringFormat format = StringFormat.GenericDefault;
            switch ((int)rotation)
            {
                case 0:
                case 360: dx = 20;  break;
                case 90: dx = 20; dy = -10; break;
                case 180: format = alignRightFormat; dx = -20; break;
                case 270: format = alignRightFormat; dx = -20; dy = -10; break;
            }

            Point pMod = new Point(p.X + dx - 20, p.Y + dy - 8);
            Rectangle rec = new Rectangle(pMod, new Size(40, 16));
            g.DrawString(card, SystemFonts.DefaultFont, Brushes.Black, rec, format);
        }

        private void PaintName(Graphics g, Point ini, Point fin)
        {
            Point pCentro = new Point((ini.X + fin.X) / 2, (ini.Y + fin.Y) / 2 - 15);
            g.DrawString("hellowWorld", SystemFonts.DialogFont, Brushes.Black, pCentro, alignCenterFormat);
        }

        private void PaintLine(Graphics g, Point ini, Point end)
        {
            g.DrawLine(myPen, ini, end);
        }

        protected virtual void PaintEndSymbol(Graphics g, Point finalCoord, float rotation)
        {
            g.TranslateTransform(finalCoord.X, finalCoord.Y);
            g.RotateTransform(rotation);
            g.FillPolygon(myPen.Brush, finalPolygon);
            g.ResetTransform();
        }
        
        public void SetIniClass(ClassView newClass)
        {
            iniClass = newClass;
        }

        public void SetEndClass(ClassView newClass)
        {
            endClass = newClass;
        }
    }
}

